package codeforcesbot

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"
)

type Contest struct {
	Name string
	Phase string
	DurationSeconds int64
	StartTimeSeconds int64
}

type CFProblem struct {
	ContestId int
	Index string
	Name string
	Rating int
}

type Participant struct {
	Handle string
}

type Team struct {
	ContestId int
	Members []Participant
}

type Submission struct {
	Id int64
	ContestId int64
	CreationTimeSeconds int64
	RelativeTimeSeconds int64
	ProgrammingLanguage string
	Verdict string
	TimeConsumedMillis int64
	MemoryConsumedBytes int64
	Problem CFProblem
	Author Team
}

type ContestSelector func(*Contest) bool

func isUpcoming(c *Contest) bool {
	if c.Phase == "BEFORE" && c.StartTimeSeconds - time.Now().Unix() <= 604800 {
		return true
	}
	return false
}

func (c Contest) String() (s string) {
	s = s + c.Name
	s = s + "\n开始时间: " + time.Unix(c.StartTimeSeconds, 0).Format(time.UnixDate) + "\n时长: "
	s = s + fmt.Sprintf("%02d: %02d\n", c.DurationSeconds / 3600, (c.DurationSeconds / 60) % 60)
	return s

}

type ContestRequest struct {
	Status string
	Result []Contest
}

type SubmissionRequest struct {
	Status string
	Result []Submission
}


var __contests []Contest
var __lst_update int64
func getContests() (contests []Contest) {
	r, err := http.Get("https://codeforces.com/api/contest.list")
	if err == nil && r.StatusCode == http.StatusOK {
		body, _ := ioutil.ReadAll(r.Body)
		//fmt.Printf("%+v\n", string(body))
		var c ContestRequest
		json.Unmarshal(body, &c)
		//fmt.Printf("%+v\n", c)
		contests = c.Result
	} else {
		fmt.Printf("ERROR status: %d\n", r.StatusCode)
	}
	__contests = contests
	__lst_update = time.Now().Unix()
	return contests
}

func getContestsCached() []Contest {
	const TIME_ALIVE int64 = 3600
	now := time.Now().Unix()
	if now - __lst_update > TIME_ALIVE {
		getContests()
	}
	return __contests
}

func getRecentSubmissions() (submissions []Submission) {
	r, err := http.Get("https://codeforces.com/api/problemset.recentStatus?count=1000")
	if err == nil && r.StatusCode == http.StatusOK {
		body, _ := ioutil.ReadAll(r.Body)
		var s SubmissionRequest
		json.Unmarshal(body, &s)
		//fmt.Printf("%+v\n", c)
		submissions = s.Result
	} else {
		fmt.Printf("ERROR status: %d\n", r.StatusCode)
	}
	return submissions
}

func formatContests(contests []Contest, selector ContestSelector) (count int, s string){
	for _, singleContest := range contests {
		if selector(&singleContest) {
			s = s + fmt.Sprintln(singleContest)
			count ++
		}
	}
	return count, s
}

func getUserSubmissions(handle string, count int) []Submission {
	r, err := http.Get(fmt.Sprintf("https://codeforces.com/api/user.status?handle=%s&from=1&count=%d", handle, count))
	if err == nil && r.StatusCode == http.StatusOK {
		body, _ := ioutil.ReadAll(r.Body)
		var s SubmissionRequest
		json.Unmarshal(body, &s)
		//fmt.Printf("%+v\n", c)
		return s.Result
	} else {
		if err == nil {
			fmt.Printf("ERROR status: %d\n", r.StatusCode)
		} else {
			fmt.Println(err.Error())
		}
	}
	return nil
}

func getSubmissionJson(submission Submission) string {
	color := "777777"
	if submission.Verdict == "OK" {
		submission.Verdict = "Accepted"
		color = "00aa00"
	}
	return fmt.Sprintf("[CQ:xml,data=<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><msg serviceID=\"1\" templateID=\"0\" action=\"web\" brief=\"&#91;提交记录&#93;\" sourceMsgId=\"0\" url=\"http://codeforces.com/contest/%d/submission/%d\" flag=\"0\" adverSign=\"0\" multiMsgFlag=\"0\"><item layout=\"6\" advertiser_id=\"0\" aid=\"0\"><title size=\"34\">%s's Submission</title><summary size=\"26\">%s\n%v ms\t%v KB</summary><hr hidden=\"false\" style=\"0\" /><summary size=\"26\" color=\"#%s\">%s</summary></item><source name=\"Codeforces\" icon=\"http://codeforces.com/favicon.png\" action=\"\" appid=\"-1\" /></msg>,resid=1]", submission.ContestId, submission.Id, submission.Author.Members[0].Handle, submission.Problem.Name, submission.TimeConsumedMillis, submission.MemoryConsumedBytes / 1024, color, submission.Verdict)

}

func countStuckSubmissions(submissions []Submission) (inQueue int, testing int) {
	for _, val := range submissions {
		if val.Verdict == "TESTING" {
			testing ++
		} else if val.Verdict == "" {
			inQueue ++
		}
	}
	return inQueue, testing
}

func getHeatValue(handles []string) (heatValue int64) {
	submitted := make(map[string]int64)
	nowSeconds := time.Now().Unix()
	for _, handle := range handles {
		submissions := getUserSubmissions(handle, 1000000000)
		fmt.Printf("%d submissions received.\n", submissions)
		for _, sub := range submissions {
			if sub.Verdict == "OK" {
				problemId := fmt.Sprint(sub.Problem.ContestId) + sub.Problem.Index
				if val, ok := submitted[problemId]; ok {
					heatValue -= int64(sub.Problem.Rating) * val
				}
				if bonus := int64(sub.Problem.Rating) - (nowSeconds - sub.CreationTimeSeconds) / 3600; bonus > 0 {
					heatValue += int64(sub.Problem.Rating) * bonus
					submitted[problemId] = bonus
				} else {
					heatValue += int64(sub.Problem.Rating)
					submitted[problemId] = 1
				}
			}
		}
	}
	return heatValue
}

func getRegisteredGroups() []int64 {
	return SqlGetNotifiedGroup()
}

func isTodaysContest(c *Contest) bool {
	y1, m1, d1 := time.Now().Date()
	y2, m2, d2 := time.Unix(c.StartTimeSeconds, 0).Date()
	return y1 == y2 && m1 == m2 && d1 == d2
}

func isContestInHour(c *Contest) bool {
	nowSeconds := time.Now().Unix()
	return c.StartTimeSeconds - nowSeconds <= 3600 && c.StartTimeSeconds > nowSeconds
}

func setupNotification(contests []Contest) {
	nowSeconds := time.Now().Unix()
	for _, contest := range contests {
		if contest.StartTimeSeconds > nowSeconds && contest.StartTimeSeconds - nowSeconds <= 60 * 60 * 24 {
			go addTimer(time.Until(time.Unix(contest.StartTimeSeconds -60 * 60, 0)), notifyContestsInHour)
		}
	}
}

func notifyTodaysContests(t time.Time) {
	fmt.Println("notify todays contests")
	groups := getRegisteredGroups()
	contests := getContestsCached()
	count, s := formatContests(contests, isTodaysContest)
	dateString := time.Now().Format("2006 年 1 月 2 日")
	if count <= 0 {
		s = "今天CF没有比赛，该摸了。"
	}
	for _, g := range groups {
		sendGroupMessage(g, StringToImg(fmt.Sprintf("早上好！今天是 %s。\n今天有%d场比赛:\n%s", dateString, count, s)))
	}

	setupNotification(contests)
}

func notifyContestsInHour(t time.Time) {
	groups := getRegisteredGroups()
	contests := getContestsCached()
	count, s := formatContests(contests, isContestInHour)
	if count <= 0 {
		return
	}
	for _, g := range groups {
		sendGroupMessage(g, fmt.Sprintf("以下比赛将在1小时内开始，请做好准备。\n%s", s))
	}
}
/*
json.

[CQ:json,data={"app":"com.tencent.structmsg"&#44;;"config":{"autosize":true&#44;;"ctime":1636448686&#44;;"forward":true&#44;;&#44;;"type":"normal"}&#44;;"desc":"新闻"&#44;;"meta":{"news":{"action":""&#44;;"android_pkg_name":""&#44;;"app_type":1&#44;;"appid":100792067&#44;;"desc":"点击加入，与好友协作文档"&#44;;"jumpUrl":"https://kdocs.cn/el/eucf3fsB8DVh?f=201"&#44;;"preview":"https://qn.cache.wpscdn.cn/s1/images/ficons/icons-xls.png"&#44;;"source_icon":""&#44;;"source_url":""&#44;;"tag":"WPSOffice"&#44;;"title":"邀请你一起编辑「419座位登记」"}}&#44;;"prompt":"&#91;分享&#93;邀请你一起编辑「419座位登记」"&#44;;"ver":"0.0.0.1"&#44;;"view":"news"}]
 */